#include "../../includes/model/ServidorArquivos.h"

namespace model
{
	ServidorArquivos::ServidorArquivos( QString &path, QObject *parent )
	: QObject( parent ), path( path )
	{
		QString *e = new QString();

		dir = new QDir( path );

		if( ! dir->exists() )
		{
			e->append( QString( "Nao e possivel ler base de arquivos em " ).append( path ) );

			delete dir;
			throw e;
		}

		delete e;
	}

	ServidorArquivos::~ServidorArquivos()
	{
		delete dir;
	}

	Arquivo* ServidorArquivos::_getArquivo( const QString &nome )
	{
		Arquivo* a;

		mutex.lock();

		if( arquivos.contains( nome ) )
		{
			a = arquivos[ nome ];
		} else
		{
			a = NULL;
		}

		mutex.unlock();

		return a;
	}

	Arquivo* ServidorArquivos::getArquivo( const QString &nome, const ModoAbertura &modo )
	{
		QString nomeCompleto = dir->absolutePath() + dir->separator().toAscii() + nome;
		Arquivo *a = _getArquivo( nome );
		QFile *f;
		bool newFile = false;

		if( a == NULL )
		{
			newFile = true;
			f = new QFile( nomeCompleto );

			if( ! f->exists() )
			{
				QString *e = new QString();

				e->append(
					QString( "Arquivo %1 nao existe." ).arg( nome )
				);

				delete f;
				throw e;
			}

			a = new Arquivo( nome, nomeCompleto, modo, this );
			connect( a, SIGNAL(fecharArquivo(Arquivo*)), this, SLOT(fecharArquivo(Arquivo*)) );

			mutex.lock();

			arquivos.insert( nome, a );

			mutex.unlock();
		}

		if( ! newFile )
		{
			if( modo == escrita && a->getNumUsuarios() != 0 )
			{
				QString *e = new QString();

				e->append(
					QString( "Arquivo %1 ja esta sendo usado. Nao e possivel abri-lo em modo de escrita" ).arg( nome )
				);

				throw e;
			}

			if( a->getModoAbertura() == escrita )
			{
				QString *e = new QString();

				e->append(
					QString( "Arquivo %1 esta bloqueado. Nao e possivel abri-lo." )
					.arg( nome )
				);

				throw e;
			}

			a->atualizaDados();
		}

		return a;
	}

	QList<QPair<QString, ModoAbertura> >* ServidorArquivos::listarDiretorio()
	{
		QStringList arquivos = dir->entryList( QDir::Files | QDir::NoDotAndDotDot );
		QString nomeArquivo;
		Arquivo *arquivo;
		QList<QString>::iterator it;
		QList<QPair<QString, ModoAbertura> > *lista = new QList<QPair<QString, ModoAbertura> >();

		for( it = arquivos.begin(); it != arquivos.end(); ++it )
		{
			nomeArquivo = *it;

			arquivo = _getArquivo( nomeArquivo );

			if( arquivo == NULL )
			{
				lista->push_back( QPair<QString, ModoAbertura>( nomeArquivo, fechado ) );
			}
			else
			{
				lista->push_back( QPair<QString, ModoAbertura>( arquivo->getNome(), arquivo->getModoAbertura() ) );
			}
		}

		return lista;
	}

	bool ServidorArquivos::fileExists( const QString &nome )
	{
		QString nomeAbsoluto = dir->absolutePath().append( dir->separator().toAscii() + nome );
		QFile f( nomeAbsoluto );

		return f.exists();
	}

	Arquivo* ServidorArquivos::adicionarArquivo( const QString& nome, const QByteArray& array )
	{
		QString nomeAbsoluto = dir->absolutePath() + dir->separator().toAscii() + nome;
		QFile *f = new QFile( nomeAbsoluto );

		if( f->exists() )
		{
			QString *s = new QString();
			s->append(
				QString( "Arquivo %1 ja existe." ).arg( nome )
			);

			delete f;
			throw s;
		}

		f->open( QIODevice::WriteOnly );

		if( (qint64) array.size() != f->write( array ) )
		{
			QString *s = new QString();
			s->append(
				QString( "Erro ao gravar Arquivo %1." ).arg( nome )
			);

			f->remove();
			f->close();
			delete f;

			throw s;
		}

		f->close();
		delete f;

		try
		{
			Arquivo *a = new Arquivo( nome, nomeAbsoluto, leitura );
			connect( a, SIGNAL(fecharArquivo(Arquivo*)), this, SLOT(fecharArquivo(Arquivo*)));

			// Proteger o acesso a lista de arquivos
			mutex.lock();

			arquivos.insert( nome, a );

			mutex.unlock();

			return a;
		} catch( QString *s )
		{
			controller::Logger::write( *s );
			throw s;
		}
	}

	QList<Arquivo*>* ServidorArquivos::getArquivoPorExt( const QString &ext, const ModoAbertura &modo )
	{
		QList<Arquivo*> *lista = new QList<Arquivo*>();
		QList<QString> fileNames;
		QList<QString>::iterator it;
		QString nome, nomeCompleto;
		Arquivo *temp = NULL;

		fileNames = dir->entryList( QDir::Files | QDir::NoDotAndDotDot );

		for( it = fileNames.begin(); it != fileNames.end(); it++ )
		{
			nome = *it;

			if( ext != "*" && ! nome.endsWith( ext ) )
			{
				continue;
			}

			temp = _getArquivo( nome );

			if( temp != NULL )
			{
				// Bloqueado
				if( temp->getModoAbertura() == escrita )
				{
					continue;
				}

				// Nao pode bloquear
				if( modo == escrita && temp->getNumUsuarios() != 0 )
				{
					continue;
				}
				temp->atualizaDados();
				lista->push_back( temp );
			} else
			{
				// Arquivo ainda nao foi aberto...
				nomeCompleto = dir->absolutePath() + dir->separator().toAscii() + nome;
				temp = new Arquivo( nome, nomeCompleto, modo, this );
				connect( temp, SIGNAL(fecharArquivo(Arquivo*)), this, SLOT(fecharArquivo(Arquivo*)));

				mutex.lock();
				arquivos.insert( nome, temp );
				mutex.unlock();

				lista->push_back( temp );
			}
		}

		return lista;
	}

	void ServidorArquivos::renomearArquivo( Arquivo *origem, const QString &destino )
	{
		QString destinoAbsoluto = dir->absolutePath() + dir->separator().toAscii() + destino;
		Arquivo *arquivo = _getArquivo( destino );

		if( arquivo != NULL )
		{
			QString *s = new QString();

			s->append(
				QString( "Ja existe um arquivo com esse nome %1. Nao e possivel renomear." )
				.arg( destino )
			);

			throw s;
		}

		origem->renomear( destino, destinoAbsoluto );
	}

	void ServidorArquivos::apagarArquivo( Arquivo *arq )
	{

		arq->apagar();

		mutex.lock();

		arquivos.remove( arq->getNome() );

		mutex.unlock();

		delete arq;
	}

	void ServidorArquivos::fecharArquivo( Arquivo *a )
	{
		mutex.lock();

		if( arquivos.contains( a->getNome() ) )
		{
			arquivos.remove( a->getNome() );
		}

		mutex.unlock();

		a->deleteLater();
	}

	void ServidorArquivos::listaArquivosAbertosClientes()
	{
		QHash<QString, Arquivo*>::iterator it;
		model::Arquivo *arquivo = NULL;

		mutex.lock();

		if( arquivos.isEmpty() )
		{
			controller::Logger::write(
				QString( "Nenhum arquivo aberto.")
			);
		} else
		{
			for( it = arquivos.begin(); it != arquivos.end(); it++ )
			{
				arquivo = *it;

				controller::Logger::write(
					QString( "Arquivo %1: %2 usuarios.")
					.arg( arquivo->getNome() ).arg( arquivo->getNumUsuarios() )
				);
			}
		}

		mutex.unlock();
	}
}
